grammar IsaToBCG; // -*- java -*-
// This file is part of HPBCG
// Grammar for an isa description
@header{
    import java.io.*;
    import java.util.Vector;
    import java.util.Calendar;
    import java.util.Date;
    import java.text.*;
    import java.util.regex.*;
}
@members{
    static boolean OptionDebug;
    static int OptionAction;
    final static int OPTMACROS = 1;
    final static int OPTLIST   = 2;
    final static int OPTVALID  = 3;
    String archName;
    int archLenght;
    Vector instructionList = new Vector();
    Instruction cI;
    public static void main(String[] args) 
    {
        try 
	{
	    ANTLRInputStream  input  = getInput (args);
	    IsaToBCGLexer     l      = new IsaToBCGLexer(input);
	    CommonTokenStream tokens = new CommonTokenStream(l);
	    IsaToBCGParser    p      = new IsaToBCGParser(tokens);
	    p.isafile();	
	}
	catch (FileNotFoundException e)
	{
	    System.err.println("File not found");
	    System.exit(-1);
        }
	catch (Exception e)
	{
	    e.printStackTrace(System.out);
	}
    }
    /**
     * Set processor name and instruction lenght
     **/
    public void setNameAndLenght (String archName, String isaLen)
    {
	this.archName = archName;
	if ("var".equals(isaLen))   this.archLenght = -1;
	else			    this.archLenght = Integer.parseInt(isaLen);
    }
    public String toString()
    {
	StringBuffer tmp = new StringBuffer(header());
	tmp.append("#ifndef WITH_HPBCG_FUNCTIONS\n");
	for (int i = 0; i < instructionList.size(); ++i)
	    tmp.append(((Instruction) instructionList.elementAt(i)).toMacro());
	tmp.append("#else /* WITH_HPBCG_FUNCTIONS */\n");
	for (int i = 0; i < instructionList.size(); ++i)
	    tmp.append(((Instruction) instructionList.elementAt(i)).toFunction());
	tmp.append("#endif /* WITH_HPBCG_FUNCTIONS */\n");
	return tmp.toString();
    } /* toString() */

    String header()
    {
	
	StringBuffer tmp = new StringBuffer("/* Autogenerated file, do not edit ! */\n");
	Calendar maintenant = Calendar.getInstance();
	SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss" );
	tmp.append("/* Generated on : "+sdf.format(new Date())+" */\n");
	tmp.append("/* "+ archName +" "+ archLenght+ " bits  */\n");
	tmp.append("#include <hpbcg-"+archName+"-utils.h>\n");
	return tmp.toString();
    } /* header */

    public String getInsnList()
    {
	String tmp = "";

	for (int i = 0; i < instructionList.size(); ++i)
	    tmp += ((Instruction) instructionList.elementAt(i)).getName()+ "\n";
	return tmp;
    } /* getInsnList */

    public boolean checkLength()
    {
	boolean isOk = true;
	int len;
	Instruction insn;
	for (int i = 0; i < instructionList.size(); ++i)
	    {
		insn = (Instruction) instructionList.elementAt(i);
		len = insn.getLength();
		if (archName.equals("x86"))
		    {
			if (0 != (len \% 8))
			    {
				isOk = false;
				System.err.println("Incorrect size "+len+" (should be multiple of 8) for insn " +insn.getName());
			    }
		    }
		else
		    {
			if (archLenght != len)
			    {
				isOk = false;
				fatalMsg("Incorrect size "+len+" (should be "+archLenght+") for insn " +insn.getName());
			    }
		    }
	    }
	return isOk;
    } /* checkLength */

    public static ANTLRInputStream getInput(String argv[]) 
	throws java.io.FileNotFoundException,
	java.io.IOException
    {
	ANTLRInputStream  inFile = null;
	
	for (int i = 0; i < argv.length; i++)
	{
	    if ('-' == argv[i].charAt(0))
		switch (argv[i].charAt(1))
		{
		case 'd': OptionDebug = true; break;
		case 'm': OptionAction = OPTMACROS; break;
		case 'l': OptionAction = OPTLIST; break;
		case 'v': OptionAction = OPTVALID;  break;
		default:  System.out.println("IsaToBCG -[m|v|d] [File.isa]\n");
		}
	    else
		inFile = new ANTLRInputStream(new FileInputStream(argv[i]));
	}
	if (null == inFile) 
	    return new ANTLRInputStream(System.in);
	else
	    return inFile;
    }
    public void Verify()
    {
	if (null == cI) 
	    cI = new Instruction(archName);
    }
    void addInstruction()
    {
	instructionList.add (cI);
	cI = null;
    } /* addInstruction */
    void addName(String insnName)
    {
	cI.setName(insnName);
    } /* addName */

    void addBinaryNumber(String n)
    {
	Verify();
	cI.addBinaryNumber(Integer.parseInt(n, 2), n.length());
    } /* addBinaryNumber */

    void addBinaryIntExpr (String n)
    {
	int i; 
	String expr = "", intEnd = "" , intStart = "";;
	Verify();

	for (i = 1; ')' != n.charAt(i) ; ++i)
	    expr += n.charAt(i);
	// Pass ")_" 
	for (i+= 2; '-' != n.charAt(i) ; ++i)	    intEnd += n.charAt(i);
	for (i++; i < n.length(); ++i)		    intStart += n.charAt(i);
	cI.addBinaryIntExpr(expr, Integer.parseInt(intStart), Integer.parseInt(intEnd));
    }
    /**
     * Parse a number description 
     * Number as iN_E-S form
     * N = Integer number E = End bit number S = Start bit number
     */
    Pattern intDescriptionPattern = Pattern.compile("i([0-9]+)_([0-9]+)(-([0-9]+))?");
    void addBinaryIntDescr(String n)
    {
	Verify();
	Matcher m = intDescriptionPattern.matcher(n);
	if (m.find())
	    {
		if (null == m.group(4))
		    cI.addBinaryIntDescr(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)), Integer.parseInt(m.group(2)));
		else
		    cI.addBinaryIntDescr(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(4)), Integer.parseInt(m.group(2)));
	    }
	else
	    {
		fatalMsg(n+" does not match a integer description");
	    }
    } /* addBinaryIntDescr */

    /**
     * 	Parse a register name 
     * regName RegNumber _ RegNumberLen  or regName RegNumber _ RegEnd  - RegStart
     **/
    Pattern regDescriptionPattern = Pattern.compile("([bfpqr])([0-9]+)_([0-9]+)(-([0-9]+))?");
    void addBinaryRegDescr(String descr)
    {
	Matcher m = regDescriptionPattern.matcher(descr);
	if (m.find())
	    {
		if (null == m.group(5))
		    cI.addBinaryRegDescr(m.group(1), Integer.parseInt(m.group(2)), Integer.parseInt (m.group(3)));
		else
		    cI.addBinaryRegDescr(m.group(1), Integer.parseInt(m.group(2)), Integer.parseInt (m.group(5)),
					 Integer.parseInt (m.group(3)));
	    }
	else
	    {
		fatalMsg(descr+" does not match a register description");
	    }
    } /* addBinaryRegDescr */

    void addAsmReg(String r)
    {
	cI.addAsmReg(r);
    } /* addAsmReg */

    void addAsmInt(String r)
    {
	cI.addAsmInt(r);
    } /* addAsmInt */
    void errMsg(String msg)
    {
	System.err.println(msg);
    } /* errMsg */
    void fatalMsg(String msg)
    {
	errMsg(msg);
	System.exit(-1);
    } /* errMsg */
    void dumpResult()
    {

	switch (OptionAction)
	{
	case OPTMACROS:   System.out.println(toString());   			break;
	case OPTLIST: 	  System.out.println(getInsnList());	break;
	case OPTVALID: 	  checkLength();				break;
	default: 
	    System.out.println("What do you want ? ");	
	    System.out.println("IsaToBCG -[m|v|d] [File.isa]\n");
	    System.exit(0);
	}
    }
}

isafile 	: isaline* EOF {dumpResult();};
isaline 	: ( addrmodlinedesc | isaarchlen | isalinedesc | COMMENT | ) NL;
// Adressing mode description
addrmodlinedesc	: ( 'ADDRMOD' addrmodline);
addrmodline	: ( addrmodbinpart CUT addrmodasmpart) ;
addrmodbinpart  : ( binnum | intdescr | regdescr )+; 
addrmodasmpart  : ('addr' ((addrmodreglist '{' addrmodreglist '}') | ('{' addrmodreglist '}') | addrmodreglist)) ;
addrmodreglist  : (REGNAME | INTNAME)+;
// Isa description
isalinedesc	: isabinpart   CUT isaasmpart  	{addInstruction();};
isaarchlen	: archname=   ('power4' | 'sparc' | 'ia64' | 'cell' | 'x86' | 'armthumb')   myIsaLen=(INT | 'var')
						{ setNameAndLenght($archname.getText(), $myIsaLen.getText());};
isabinpart 	: (binnum | intdescr | regdescr | paropen)+;
binnum 		: BINNUM  			{ addBinaryNumber  ($BINNUM.getText());		};
intdescr 	: INTDESCR 			{ addBinaryIntDescr($INTDESCR.getText());	};
paropen		: PAROPEN 			{ addBinaryIntExpr ($PAROPEN.getText());	};
regdescr 	: REGDESCR	 		{ addBinaryRegDescr($REGDESCR.getText());	};
// 
INTDESCR        : 'i' INT '_' INT ('-' INT)? 							;
REGDESCR	: REGLETTER INT '_' INT	('-' INT)? 						;
PAROPEN		: '(' (~(')'))*	')_' INT '-' INT						;
isaasmpart 	: INSNNAME paramlist 		{ addName($INSNNAME.getText());			};
paramlist 	: (param)*									;
param 		: (intname | regname | BINNUM)							;
regname		: REGNAME			{ addAsmReg($REGNAME.getText());		};
intname		: INTNAME			{ addAsmInt($INTNAME.getText());		};
REGNAME		: (REGLETTER (INT) | ('ax' | 'eax' | 'al' | 'sp' | 'pc')) 			;
INTNAME		: 'i' (INT)					;
INSNNAME	: (LETTER)(LETTER | NUM)*			;
BINNUM 		: ('0' | '1')+ 					;
INT 		: (NUM)+ 					;
SPACE 		: ( '[' | ']' | '=' | ',' | ' ' | '\t')+  { skip(); 	}		;
NL 		: ('\n' | '\r')					;
COMMENT		: '#' (~(NL))*					;
CUT    		: '|' 						;
fragment LETTER	: ('a'..'z'|'A'..'Z' | '.') 			;
fragment REGLETTER: ('b' | 'f' | 'p' | 'q' | 'r')		;
fragment NUM 	: ('0'..'9') 					;
